package com.lsa.expression.parser.javacc.eval;

import com.lsa.expression.parser.javacc.*;

public class EvalWalker
{
    public Object walk(Node node) throws Exception
    {
        Object result = null;
        if (node instanceof ASTExpression)
        {
            if (node.jjtGetNumChildren() == 1)
            {
                result = walk(node.jjtGetChild(0));
            }
            else
            {
                throw new ParseException("Unexpected number of expr childs!");
            }
        }
        else if (node instanceof ASTLiteral)
        {
            result = ((SimpleNode)node).data;
        }
        else if (node instanceof ASTNotNode)
        {
            result = walk(node.jjtGetChild(0));
            if (!(result instanceof Boolean))
            {
                throw new ParseException("Data type error");
            }
            result = !(Boolean)result;
        }
        else if (node instanceof ASTMulNode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first * operands.second;
        }
        else if (node instanceof ASTDivNode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first / operands.second;
        }
        else if (node instanceof ASTModNode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first % operands.second;
        }
        else if (node instanceof ASTAddNode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first + operands.second;
        }
        else if (node instanceof ASTSubtractNode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first - operands.second;
        }
        else if (node instanceof ASTLTNode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first < operands.second;
        }
        else if (node instanceof ASTLENode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first <= operands.second;
        }
        else if (node instanceof ASTGTNode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first > operands.second;
        }
        else if (node instanceof ASTGENode)
        {
            OperandGetter<Integer> opGet = new OperandGetter<Integer>(Integer.MAX_VALUE);
            Pair<Integer> operands = opGet.getPairedOperands(node);
            
            result = operands.first >= operands.second;
        }
        else if (node instanceof ASTEQNode)
        {
            Pair operands = getUntypedOperands(node);
            result = operands.first.equals(operands.second);
        }
        else if (node instanceof ASTNEQNode)
        {
            Pair operands = getUntypedOperands(node);
            result = !operands.first.equals(operands.second);
        }
        else if (node instanceof ASTAndNode)
        {
            OperandGetter<Boolean> opGet = new OperandGetter<Boolean>(Boolean.TRUE);
            Pair<Boolean> operands = opGet.getPairedOperands(node);
            result = operands.first && operands.second;
        }
        else if (node instanceof ASTOrNode)
        {
            OperandGetter<Boolean> opGet = new OperandGetter<Boolean>(Boolean.TRUE);
            Pair<Boolean> operands = opGet.getPairedOperands(node);
            result = operands.first || operands.second;
        }
        
        return result;
    }
    
    private class OperandGetter<T>
    {
        T sample;
        
        public OperandGetter(T sample)
        {
            this.sample = sample;
        }

        public Pair<T> getPairedOperands(Node node) throws Exception
        {
            return new Pair<T>(getOperand(node, 0), getOperand(node, 1));
        }
        
        public T getOperand(Node node, int i) throws Exception
        {
            Object operand = walk(node.jjtGetChild(i));
            if (operand == null || !operand.getClass().isAssignableFrom(this.sample.getClass()))
                throw new ParseException("Data type error");
            
            return (T)operand;
        }
    }
    
    Pair getUntypedOperands(Node node) throws Exception
    {
        Object o1 = walk(node.jjtGetChild(0));
        Object o2 = walk(node.jjtGetChild(1));
        return new Pair(o1, o2);
    }
    
    private static class Pair<T>
    {
        public T first;
        public T second;
        
        public Pair()
        {
        }

        public Pair(T first, T second) 
        {
            this.first = first;
            this.second = second;
        }
    }
}

